Why Angular2

  • Expressive HTML
  • Powerful Data Binding
  • Modular by Design
  • Built-in Back-end Integration

alt


TypeScript

  • Typescript is a Superset of Javascript

  • Transpiles to plain Javascript

  • Open-source language

  • Strongly Type (强类型语言)

    @Component {
      pageTitle: string = 'test';
      product: any[] = {...}
    }
    

    we can also custom a storngly data type for certain data type, like below:

    // product.ts
    
    export interface IProduct {
        productId: number,
        productName: string,
        productCode: string,
        releaseDate: string,
        description:string,
        price: number,
        starRating: number,
        imageUrl: string
    }
    
    // we are able to implement it with method here.
    

    then we have a data type IProduct, which can be used in Angular2

    import IProduct from './product.ts';
    
    @Component {
      pageTitle: string = 'test';
      product: IProduct[] = {...}
    }
    

  • Class-based object-orientation

Angular2 can also be compiled with Dart which is not a Javascript Language


Component

alt

import { Component } from '@angular/core';			// Import Component decrator from Angular

@Component({						// Matedata and Template
    selector: 'pm-app',
    template: `
		<div>
			<h1>{{ pageTitle }}</h1>
			<div>My first Angular2 App</div>
		</div>
    `
})
export class AppComponent {			// Class, "AppComponent" is the component name used in the code
  pageTitle: string = "Ethan's Product Management System Demo"
}

Decorator

A function that add supplied information (metadata) to a class, its members, or its method arguments.

@classname(supplied property, supplied method, supplied parameter...)

Decorator 原属于一种设计模式,在既有的类上面新增功能,从而避免大量不必要的继承子类,是子类的一种有效代替。 例如,原本selector 属性有值,但是通过decorator 的替代,我们就可以直接换掉selector原本的内容,而不需要重新把内部定义selector的代码重复一遍或者继承一个子类。

Decorator is a ES7 feature, which only be implemented and used with Typescript.

Import

import { Component } from '@angular/core';			// Import Component decrator from Angular

Before we used any external function or class, we need to import it first with ES6 modular.

example above import Component decortaor from the @angular folder

  • @angular/core
  • @angular/animate
  • @angular/http
  • @angular/router

Metadata & Template

@Component({						// Matedata and Template
    selector: 'pm-app',
    template: `
		<div>
			<h1>{{ pageTitle }}</h1>
			<div>My first Angular2 App</div>
		</div>
    `
  	// or templateUrl: 'app/products/product-list.component.html'
})
  • @component({...}) is a component decorator

  • selector: 'pm-app' is directive name used in HTML

    <body>
      <pm-app>Loading App ...</pm-app>
    </body>
    

    when Angular is loading, the nested “Loading APP …” will show up

  • template: … is the template for view layout

    • will be automatically bind to the class preproty “pageTitle”

    • Inline Template: template: "<h1>..</h1>"

    • Linked Template: templateUrl: 'app/products/product-list.component.html'

      • templateUrl path should start from app since it will be load in ts transpile, which is the same folder with app.

      • as an alternative, we can add a moduleId attribute to use the relative path

        @Component({
          selector: 'pm-products',
          moduleId: module.id,
          templateUrl:'product-list.component.html'
          styleUrls:   ['product-list.component.css']
        })
        

Stylesheet

  • Inline stylesheet

    <img [style.margin]='imageMargin'
         [style.width.px]='imageWidth'>
    
  • Encapsulating Component Style

    @Component({
      selector: 'pm-products',
      templateUrl:'app/products/product-list.component.html'
      styles:['body {color: #337AB7}']
    })
    
    //or
    
    @Component({
      selector: 'pm-products',
      templateUrl:'app/products/product-list.component.html'
      styleUrls:   ['app/products/product-list.component.css']
    })
    

LifeCycle

import { Component, OnInit } from '@angular/core';			// Step 1

...

export class AppComponent implements OnInit{				// Step 2
  ...
  ngOnInit(): void {									  // Step 3
    console.log('In OnInit')
  }
}

Step 1 import hooks from angular Step 2 implement hook interface in class optional?Step 3 add hook method (ngOnInit) in the class

  • OnInit:act before component initialization, good time to retrieve data
  • OnChanges: perform action after change to input property
  • OnDestroy: act before angular destory components

Nest Component (嵌套组建)

If there is no data transfering, then use the component as usual.

alt

  • If child components need data from parent component, then we use nest component.

    Parent —> pass property —> Child Component WITH @Input

    import { Component, OnChanges, Input } from '@angular/core'
    
    @Component({
        selector: 'ai-star',
        moduleId: module.id,
        templateUrl: 'star.component.html',
        styleUrls: ['star.component.css']
    })
    
    export class StarComponent implements OnChanges{
        @Input() rating: number;
        starWidth: number;
    
        ngOnChanges() {
            this.starWidth = this.rating * 86 / 5;
        }
    } 
    
    <td>
      <ai-star [rating]='product.starRating'></ai-star>
    </td>
    
    • Input

      import input module from @Angular/core, to data that need to be transferred, use @input() decorator.

    • OnChanges

      use OnChanges hook to make sure different data has its own property.

      in the case above, the starWidth is a changing data based on the inputed rating, so it should be defined in the ngOnChanges hook.

  • If a child component need to trigger something from parent component, then we use @Output module

    Child —> Trigger Event —> Parent Component WITH @Output Decorator

    • STEP1 import Output and EventEmitter from ‘@Angular/core’

    • STEP2 in the child ts file

      import { Component, OnChanges, Input, Output, EventEmitter} from '@angular/core'
      
      @Component({...})
      
      export class StarComponent implements OnChanges{
          ...
      
          // add an event: ratingClicked (pass a string message)
          @Output() ratingClicked: EventEmitter<string> = new EventEmitter<string> ();
      
          // add a onClick event, once div is clicked, emit event with following string
          onClick(): void {
              this.ratingClicked.emit(`The rating ${this.rating} was click`);
          }
      }
      

      in the child HTML file

      <div (click)="onClick()">

    • STEP3 in the parent html file

      <--! now ratingClicked is an event that can be triggered by click the child component -->
      <--! and the $event params is the passing message thay can be used in other methods-->
        
      <ai-star (ratingClicked)="onRatingClick($event)"></ai-star>
      

Angular Module

// import main module from angular folder
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'

// import components from each compoenet folder
import { AppComponent }  from './app.component';
import { ProductListComponent } from './products/product-list.component'

// a decorator of AngularModual
@NgModule({
  imports: [ 			// main module
    BrowserModule,
    FormsModule
  ],
  declarations: [ 		// components
    AppComponent, 
    ProductListComponent 
  ],
  bootstrap: [ AppComponent ]	// root components
})
export class AppModule { }

alt


Data Binding

alt

Interpolation

<h1>{{ pageTitle }}</h1>
{{ 'Title: ' + pageTitle }}
{{ 2 * 20 + 1 }}
{{ 'Title: ' + getTitle() }}
<h1 innerText={{ pageTitle }}></h1>
<img src={{ imageUrl }}>
<img src={{ 'http://' + imageUrl  }}
export class AppcComponent = {
  pageTitile: string = "Hello Wrold",
  getTitle: function(){
    return this.pageTitle
  }
}

Property Binding

<img [src]='product.imageUrl'
     [style.width.px]='imageWidth'>

[src] is the binding target

’product.imageUrl’ is the binding source

Event Binding

the only binding from DOM to COMPONENT

<button (click)='toggleImage()'></button>

(click) is the target event

’toggleImage()’ is the template statement

Two-way Binding

<input [(ngModel)]='listFilter' />

[()] indicate the truth that two-way binding is the combination of property binding and event binding

ngModel is from FormModule, so we need import FormModule into appModule firstly

Pipes

pipes transform bound properties before display

following are some built-in pipes:

  • date

  • number, decimal, percent, currency

  • json, slice

    {{ price | currency | lowercase }}
    {{ price | currency:'USD':true:'1.2-2' }}
    

custom pipes

// product-filter.pipe.ts

import { PipeTransform, Pipe } from "@angular/core";
import { IProduct } from './product'

@Pipe({
    name: 'productFilter',			// pipe name that used in HTML
})

export class ProductFilterPipe implements PipeTransform {
    transform(value: IProduct[], filterBy: string): IProduct[] {
        filterBy = filterBy ? filterBy.toLocaleLowerCase() : null;
        return filterBy ? value.filter((product: IProduct[]) =>
            product.productName.toLocaleLowerCase().indexOf(filterBy) !== -1) : value;
    }
}
  • create a new ts file for each custom pipe
  • import PipeTransform and Pipe module from angualr core
  • create a Pipe decorator
  • implement PipeTransform with transform function

Built-in Directives

Structural Directives

from the Browser Module

  • *ngIf: If logic

    <h1 *ngIf='products && products.length'></h1>
    
  • *ngFor: Loop logic

    <tr *ngFor="let product of products">
      <td></td>
      <td>{{ product.productName }}</td>
      <td>{{ product.productCode }}</td>
      <td>{{ product.releaseDate }}</td>
      <td>{{ product. price}}</td>
      <td>{{ product. starRating}}</td>
    </tr>
    

    for… of VS for… in

    • for… of… : iterates over iterable objects, such as array, return true array element
    • for… in…: interates over properities of an object, return index of array element

Service & DI

Service: A class with a focused purpose, which used for features:

  • are independent from any particular components
  • provide shard data or logic cross components
  • Encapsulate external interactions

How to build a Service (similar to build a component)

  • Step1: create the service class
  • Step2: defind metadata with a decorator named @Injectable()
  • Step3: import what we need, like Injectable from ‘@Angular/core’
import { Injectable } from '@angular/core'

@Injectable()

export class ProductService {
    getProduct(): IProduct[] {
        return ...;
    }
}

How to register a Service

if service been registered into a component, then this component and its children can use this service

if service been registered into the Angular root component, then this service can be used everywhere

import { Component } from '@angular/core';
import {ProductService} from "./products/product.service";

@Component({
  ...
    providers: [ProductService]  // registeration
})

How to use Service in a component (Dependency Injection)

implement DI in the constructor of class. (constructor only used fot init the instance)

import { Component } from '@angular/core';
import { ProductService } from "./products/product.service";

@Component({...})
            
export class ProductListComponent implements OnInits{
    
    // private _productService;
    // constructor (productService: ProductService) {
    //	   _productService = productService	
    // }
    // 
	// 			EQUALS TO  
   
    constructor (private _productService: ProductService) {
    }
           
    ngOnInit(): void {
       this.product = this._productService.getProduct();
    }
}

we call _productService in OnInit since constructor should only be used to init the class


HTTP Request

  • Step1: import a ‘HttpModule’ module from ‘@Angular/http’ into Angular APP Module

  • Step2: use http module to send request in certain component / service

    // in product.service.ts
    
    ...
    
    import {Http, Response} from "@angular/http";
    import {Observable} from "rxjs";
    import 'rxjs/add/operator/map'
    
    @Injectable()
    
    export class ProductService {
        private _productUrl = 'www.baidu.com/product';
    
        constructor (private  _http: Http) { }
    
        getProducts(): Observable<IProduct[]> {
            var response = this._http.get(this._productUrl);
          	return response.map((response: Response) => <IProduct[]>response.json());
        }
    }
    


Routing

  • Declear in root module

    ...
    import { RouterModule } from '@angular/router';
    
    @NgModule({
      imports: [
        ...,
        RouterModule.forRoot([routeConfig], {useHash: true})
      ]
    })
    
  • Configure Route

    const routeConfig = [
      { path: 'products', component: ProductListComponent },
      { path: 'products/:id', component: ProductDetailComponent },
      { path: '', component: HomeComponent },
      { path: '**', component: PageNotFoundComponent },
    ]
    

    Route Configuration can also be placed inside of component’s name.routes.ts

  • Use It!

<router-outlet></router-outlet>					/* where the linked content show up */
<a routerLink="/products">Product List</a>
{ path: 'products', component: ProductionListComponent }

// product-list.component.ts
import { Component } from '@angular/core';
@Component({
  templateUrl: '....html'
})
export class ProductListComponent { }

Protect Routes with Guards

  • CanActive: Guard navigation to a route
  • CanDeactive: Guard navigation from a route
  • Resolve: Pre-fetch data before activating a route
  • CanLoad: Prevent asynchronous routing